package anaglyphe;

/*
 * LICENSE
 * 
 * JCanvas3DAnaglyph.java 
 * 
 * is a derived work of the Java 3D utility class "com.sun.j3d.exp.swing.JCanvas3D.java".
 * 
 * Redistribution and use are permitted according to the following license notice.
 * 
 * Version: 0.5
 * Date: 2009/04/29
 * 
 * Author:
 * August Lammersdorf, InteractiveMesh e.K.
 * Kolomanstrasse 2a, 85737 Ismaning
 * Germany / Munich Area
 * www.InteractiveMesh.com/org
 * 
 */

/*
 * $RCSfile: com.sun.j3d.exp.swing.JCanvas3D.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 * $Revision: 1.10 $
 * $Date: 2007/04/11 02:08:56 $
 * $State: Exp $
 * 
 *  @author: Frederic 'pepe' Barachant
 *
 */

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import javax.media.j3d.Canvas3D;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.RestrictedAccessException;
import javax.media.j3d.Screen3D;
import javax.media.j3d.View;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import outils.Constantes;

import com.sun.j3d.exp.swing.impl.AutoOffScreenCanvas3D;

/**
 * <p>
 * This class provides Java 3D rendering into a lightweight component which can
 * be added to a Swing container.
 * </p>
 * <p>
 * It allows to run the Java 3D rendering loop in parallel to the Swing painting
 * loop with synchronized minimal overlapping.
 * </p>
 * <p>
 * The following events will be redirected to the underlying heavyweight
 * Canvas3D object:<br>
 * <ul>
 * <li>InputMethodEvent</li>
 * <li>KeyEvent</li>
 * <li>FocusEvent</li>
 * <li>ComponentKeyEvent</li>
 * <li>MouseWheelEvent</li>
 * <li>MouseEvent</li>
 * <li>MouseMotionEvent</li>
 * </ul>
 * </p>
 * 
 * Serialization: JCanvas3DAnaglyphs does not support serialization.
 * 
 * @since Java 3D 1.5
 * 
 */

// License see above
public class JCanvas3DAnaglyph extends JPanel implements AncestorListener {
	private static final long serialVersionUID = 1L;

	/** size of a pixel */
	private static double METERS_PER_PIXEL = 0.0254 / 90.0;

	/** the graphics configuration used for this canvas */
	private GraphicsConfiguration graphicsConfig;

	/** The canvi that are linked to the component. */
	private InternalCanvas3D canvasLeft;
	private InternalCanvas3D canvasRight;

	//
	// Double Off-screen Buffer - see also InternalCanvas3D
	//
	/**
	 * That image of the off-screen buffers which Swing is drawing.
	 * 
	 * @see #paintComponent(Graphics)
	 */
	private BufferedImage paintImage = null;
	/**
	 * The width of the image which Swing is drawing.
	 */
	private volatile int imageWidth = 0;
	/**
	 * The height of the image which Swing is drawing.
	 */
	private volatile int imageHeight = 0;

	private long swapTimeLast = 0;
	private long swapTimeCurr = 0;
	private boolean isEyeSwapedLeft = false;
	private boolean isEyeSwapedRight = false;

	private int[] paintIntBuffer = null;
	private int[] renderIntBufferLeft = null;
	private int[] renderIntBufferRight = null;

	private ReentrantLock imageAccessLock = null;
	private Condition imageCopyCondition = null;
	private Condition imageResizeCondition = null;
	private boolean isImageCopied = false;
	private boolean isRendererLocked = false;

	private Runnable repainter = null;

	// Don't wait longer than 200 ms for repainting
	private static final int LIMIT_WAIT = 200000000;
	private static final int LIMIT_SWAP = 400000000; // 400 ms

	// private long startTimeRender = 0;
	// private long endTimeRender = 0;

	/**
	 * Constructs and initializes a new JCanvas3DAnaglyphs object that Java 3D
	 * can render into. The screen device is obtained from
	 * <code>GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()</code>
	 * , which might not be the one you should use if you are in a multiscreen
	 * environment. The JCanvas3DAnaglyphs is constructed using the following
	 * default parameters:<br>
	 * double buffer enable : false<br>
	 * stereo enable : false<br>
	 */
	public JCanvas3DAnaglyph() {
		this(null, null);
	}

	/**
	 * Constructs and initializes a new JCanvas3DAnaglyphs object that Java 3D
	 * can render into, using the specified graphics device.
	 * 
	 * @param device
	 *            the screen graphics device that will be used to construct a
	 *            GraphicsConfiguration.
	 */
	public JCanvas3DAnaglyph(GraphicsDevice device) {
		this(null, device);
	}

	/**
	 * Constructs and initializes a new JCanvas3DAnaglyphs object that Java 3D
	 * can render into, using the specified template. The screen device is
	 * obtained from
	 * <code>GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()</code>
	 * , which might not be the one you should use if you are in a multiscreen
	 * environment.
	 * 
	 * @param template
	 *            The template that will be used to construct a
	 *            GraphicsConfiguration. The stereo and doublebuffer properties
	 *            are forced to UNNECESSARY.
	 */
	public JCanvas3DAnaglyph(GraphicsConfigTemplate3D template) {
		this(template, null);
	}

	/**
	 * Constructs and initializes a new JCanvas3DAnaglyphs object that Java 3D
	 * can render into, using the specified template and graphics device.
	 * 
	 * @param template
	 *            The template that will be used to construct a
	 *            GraphicsConfiguration. The stereo and doublebuffer properties
	 *            are forced to UNNECESSARY.
	 * @param device
	 *            the screen graphics device that will be used to construct a
	 *            GraphicsConfiguration in conjunction with the template.
	 */
	public JCanvas3DAnaglyph(GraphicsConfigTemplate3D template,
			GraphicsDevice device) {

		if (template == null) {
			template = new GraphicsConfigTemplate3D();
		}
		// Force double-buffer and stereo to UNNECESSARY
		template.setStereo(GraphicsConfigTemplate.UNNECESSARY);
		template.setDoubleBuffer(GraphicsConfigTemplate.UNNECESSARY);

		if (device == null) {
			graphicsConfig = GraphicsEnvironment.getLocalGraphicsEnvironment()
					.getDefaultScreenDevice().getBestConfiguration(template);
		} else {
			graphicsConfig = device.getBestConfiguration(template);
		}

		addAncestorListener(this);
		setDoubleBuffered(false);

		// so that key events and such can be received.
		setFocusable(true);

		// Sync image access
		imageAccessLock = new ReentrantLock();
		imageCopyCondition = imageAccessLock.newCondition();
		imageResizeCondition = imageAccessLock.newCondition();

		repainter = new Repainter();
	}

	// Repainter
	/**
	 * Copies the 3D rendering image into the 2D painting image while the
	 * J3D-Renderer thread is waiting and repaints the lightweight panel.
	 */
	private class Repainter implements Runnable {
		// EDT
		public void run() {
			try {
				imageAccessLock.lock();

				// Copy 3D rendering image into 2D painting image

				// int argb = 0;

				// int aLeft = 0;
				// int rLeft = 0;
				// int gLeft = 0;
				// int bLeft = 0;

				// int aRight = 0;
				// int rRight = 0;
				// int gRight = 0;
				// int bRight = 0;
				//
				// int aPaint = 0;
				// int rPaint = 0;
				// int gPaint = 0;
				// int bPaint = 0;

				int i = 0;

				for (int j = 0; j < imageHeight; j++) {
					for (int k = 0; k < imageWidth; k++) {
						// int aNB = renderIntBufferRight[i];

						// int r = (int) (0.2125*(aNB >> 8 & 0xFF));
						// int v = (int) (0.7154*(aNB >> 4 & 0xFF));
						// int b = (int) (0.072*(aNB & 0xFF));
						// aNB = (int) r+v+b ;

						// argb = renderIntBufferLeft[i];

						// aLeft = (argb >> 24) & 0xFF;
						// rLeft = (argb >> 16) & 0xFF;
						// gLeft = (argb >> 8) & 0xFF;
						// bLeft = argb & 0xFF;

						// argb = renderIntBufferRight[i];

						// aRight = (argb >> 24) & 0xFF;
						// rRight = (argb >> 16) & 0xFF;
						// gRight = (argb >> 8) & 0xFF;
						// bRight = argb & 0xFF;

						// aPaint = (argb >> 24) & 0xFF; // aRight;
						// rPaint = (int) (0.7 * gLeft + 0.3 * bLeft);
						// gPaint = gRight;
						// bPaint = bRight;

						// paintIntBuffer[i++] = (aPaint << 24) | (rPaint << 16)
						// | (gPaint << 8) | bPaint;
						// paintIntBuffer[i++] = (aPaint << 24) | rPaint << 8;
						// paintIntBuffer[i++] = (aPaint << 24)| (rLeft << 16) |
						// (gPaint << 8) | bPaint;
						// paintIntBuffer[i++] = (aNB<<24);
						paintIntBuffer[i] = Constantes.filtreRendu
								.valeur(renderIntBufferLeft[i],
										renderIntBufferRight[i]);
						i++;
					}
				}

				// Notify the waiting J3D-Renderer thread.
				isImageCopied = true;
				imageCopyCondition.signal();
			} finally {
				imageAccessLock.unlock();
			}

			// Repaint
			JCanvas3DAnaglyph.this.repaint();

		}
	}

	private void postSwapDone(int eye) {

		// Eliminates single swap call, restart 'counting'
		swapTimeCurr = System.nanoTime();
		if ((swapTimeCurr - swapTimeLast) > LIMIT_SWAP) {
			isEyeSwapedLeft = false;
			isEyeSwapedRight = false;
			// System.out.println("JCanvas3DAnaglyphs postSwapDone : (swapTimeLast - swapTimeCurr) > LIMIT_SWAP !!!");
		}
		swapTimeLast = swapTimeCurr;

		if (eye == View.LEFT_EYE_VIEW) {
			// Double left
			if (isEyeSwapedLeft) {
				return;
			}

			isEyeSwapedLeft = true;
		}

		if (eye == View.RIGHT_EYE_VIEW) {
			// Double right
			if (isEyeSwapedRight) {
				return;
			}

			isEyeSwapedRight = true;
		}

		// System.out.println("JCanvas3DAnaglyphs postSwapDone isSwapedLeft / isSwapedRight = "
		// + isEyeSwapedLeft + " / " + isEyeSwapedRight);

		if (!isEyeSwapedLeft || !isEyeSwapedRight)
			return;

		// If locked -> EDT is resizing -> drop repaint
		if (!imageAccessLock.tryLock()) {
			isEyeSwapedLeft = false;
			isEyeSwapedRight = false;
			return;
		}

		try {
			isImageCopied = false;

			// Call 'repainter' on EDT
			SwingUtilities.invokeLater(repainter);

			// Wait until image is copied
			while (!isImageCopied) {
				isRendererLocked = true;

				try {
					imageCopyCondition.awaitNanos(LIMIT_WAIT); // Don't wait for
					// ever
					isImageCopied = true; // and release yourself
				} catch (InterruptedException e) {
				}
			}

			isRendererLocked = false;
			imageResizeCondition.signal();
		} finally {
			isEyeSwapedLeft = false;
			isEyeSwapedRight = false;

			imageAccessLock.unlock();
		}
		/*
		 * endTimeRender = System.nanoTime();
		 * System.out.println("JCanvas3DAnaglyphs render duration = "+
		 * (endTimeRender-startTimeRender)); startTimeRender = endTimeRender;
		 */
	}

	//
	// Interface AncestorListener
	//

	/**
	 * Called when the source or one of its ancestors is made visible either by
	 * setVisible(true) being called or by its being added to the component
	 * hierarchy.
	 * 
	 * The method is only called if the source has actually become visible. For
	 * this to be true all its parents must be visible and it must be in a
	 * hierarchy rooted at a Window.
	 * 
	 * @param event
	 *            {@inheritDoc}
	 */
	public void ancestorAdded(AncestorEvent event) {
		Dimension sz = getSize();

		if (sz.width == 0) {
			sz.width = 100;
		}

		if (sz.height == 0) {
			sz.height = 100;
		}

		createCanvas(sz.width, sz.height);

		canvasLeft.addNotifyFlag = true; // make it so that i can call
		// addNotify() without being
		// rejected.
		canvasLeft.addNotify();
		canvasRight.addNotifyFlag = true; // make it so that i can call
		// addNotify() without being
		// rejected.
		canvasRight.addNotify();
	}

	/**
	 * Called when either the source or one of its ancestors is moved.
	 * 
	 * @param event
	 *            {@inheritDoc}
	 */
	public void ancestorMoved(AncestorEvent event) {
	}

	/**
	 * Called when the source or one of its ancestors is made invisible either
	 * by setVisible(false) being called or by its being remove from the
	 * component hierarchy.
	 * 
	 * The method is only called if the source has actually become invisible.
	 * For this to be true at least one of its parents must by invisible or it
	 * is not in a hierarchy rooted at a Window.
	 * 
	 * @param event
	 *            {@inheritDoc}
	 */
	public void ancestorRemoved(AncestorEvent event) {
		canvasLeft.removeNotify();
		canvasRight.removeNotify();
	}

	//
	// Java 3D specific methods
	//

	/**
	 * Returns the left off-screen heavyweight canvas of this lightweight
	 * component.
	 * 
	 * No canvas is created if this component has no parent, that is, was not
	 * added to a container.
	 * 
	 * @return the left heavyweight canvas that lies in the deepness of this
	 *         Component.
	 */
	public Canvas3D getOffscreenCanvas3DLeft() {
		if (canvasLeft == null) {
			createCanvas(getWidth(), getHeight());
		}

		return canvasLeft;
	}

	/**
	 * Returns the right off-screen heavyweight canvas of this lightweight
	 * component.
	 * 
	 * No canvas is created if this component has no parent, that is, was not
	 * added to a container.
	 * 
	 * @return the right heavyweight canvas that lies in the deepness of this
	 *         Component.
	 */
	public Canvas3D getOffscreenCanvas3DRight() {
		if (canvasRight == null) {
			createCanvas(getWidth(), getHeight());
		}

		return canvasRight;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @param x
	 *            {@inheritDoc}
	 * @param y
	 *            {@inheritDoc}
	 * @param width
	 *            {@inheritDoc}
	 * @param height
	 *            {@inheritDoc}
	 */
	@Override
	public void setBounds(int x, int y, int width, int height) {

		super.setBounds(x, y, width, height);

		createCanvas(width, height);
	}

	/**
	 * Creates a heavyweight canvas and initializes it, or changes the size of
	 * the current one if present. Current heavyweight canvas is changed only if
	 * size is different from the actual one. No canvas is created if this
	 * component has no parent, that is, was not added to a container.
	 * 
	 * @param width
	 *            the width of the canvas to create.
	 * @param height
	 *            the height of the canvas to create.
	 */
	private void createCanvas(final int width, final int height) {

		if (getParent() == null) {
			return;
		}

		if (canvasLeft == null) {
			canvasLeft = new InternalCanvas3D(graphicsConfig,
					View.LEFT_EYE_VIEW);
			canvasRight = new InternalCanvas3D(graphicsConfig,
					View.RIGHT_EYE_VIEW);
		} else {
			if (canvasLeft.getWidth() == width
					&& canvasLeft.getHeight() == height)
				return;
		}

		// Create 'Double OffScreen Buffer'

		if (SwingUtilities.isEventDispatchThread()) {
			createOffScreenBuffers(graphicsConfig.getBounds(), width, height);
		} else {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					createOffScreenBuffers(graphicsConfig.getBounds(), width,
							height);
				}
			});
		}
	}

	/**
	 * 
	 * @param screenRect
	 * @param width
	 * @param height
	 */
	private void createOffScreenBuffers(Rectangle screenRect, int width,
			int height) {
		try {
			imageAccessLock.lock();

			// Setting offscreen buffer requires that the J3D-Renderer thread
			// isn't blocked
			// As we are on the EDT, 'this.run()' will not release the
			// J3D-Renderer
			// So, it's done here. We will wait until this has happened.
			while (isRendererLocked) {

				isImageCopied = true;
				imageCopyCondition.signal();

				try {
					imageResizeCondition.await();
				} catch (InterruptedException e) {
				}
			}

			canvasLeft.createOffScreenBuffers(graphicsConfig.getBounds(),
					width, height);
			canvasRight.createOffScreenBuffers(graphicsConfig.getBounds(),
					width, height);

		} finally {
			imageAccessLock.unlock();
		}
	}

	/**
	 * Flips and paints the result of the 3D rendering.
	 * 
	 * @param g
	 *            {@inheritDoc}
	 */
	@Override
	public void paintComponent(Graphics g) {

		super.paintComponent(g); // paint background

		// Draw & flip offscreen buffer

		if (paintImage != null && canvasLeft.isRendererRunning()
				&& canvasRight.isRendererRunning()) {

			g.drawImage(paintImage,
			// destination in g2d: flip lowerY and upperY
					// dx1 dy1 dx2 dy2
					0, imageHeight, imageWidth, 0,
					// source: the bottom part of the scene image
					// sx1 sy1 sx2 sy2
					0, 0, imageWidth, imageHeight, null);
		}
	}

	//
	// Override Component's processXXX - Redirects event to canvasLeft and to
	// superclass.
	//
	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processComponentKeyEvent(KeyEvent e) {
		super.processComponentKeyEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processComponentEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processFocusEvent(FocusEvent e) {
		super.processFocusEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processFocusEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processInputMethodEvent(InputMethodEvent e) {
		super.processInputMethodEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processInputMethodEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processKeyEvent(KeyEvent e) {
		super.processKeyEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processKeyEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processMouseEvent(MouseEvent e) {
		super.processMouseEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processMouseEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processMouseMotionEvent(MouseEvent e) {
		super.processMouseMotionEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processMouseMotionEvent(e);
		e.setSource(src);
	}

	/**
	 * Redirects event to canvas and to superclass.
	 * 
	 * @param e
	 *            {@inheritDoc}
	 */
	@Override
	protected void processMouseWheelEvent(MouseWheelEvent e) {
		super.processMouseWheelEvent(e);

		Object src = e.getSource();
		e.setSource(canvasLeft);
		canvasLeft.processMouseWheelEvent(e);
		e.setSource(src);
	}

	/**
	 * This class is the internal Canvas3D that is used and sent to Java 3D. It
	 * is remote controlled through JCanvas3DAnaglyphs and is modified to be
	 * able to tell the lightweight component when refreshes are needed.
	 */
	private final class InternalCanvas3D extends Canvas3D implements
			AutoOffScreenCanvas3D {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		private BufferedImage renderImage = null;

		private int eye = -1;

		/**
		 * Flag used to sort a call to addnotify() from user and from the
		 * lightweight component. Lightweight component calls addNotify() so
		 * that the rendering begins and uses normal routines, but this is a
		 * method that user must not call.
		 */
		private boolean addNotifyFlag = false;

		/**
		 * Creates a new instance of JCanvas3DAnaglyphs.
		 * 
		 * @param graphicsConfiguration
		 *            The graphics configuration to be used.
		 * @param lwCanvas
		 *            the lightweight canvas that is linked to that heavyweight
		 *            canvas.
		 */
		private InternalCanvas3D(GraphicsConfiguration graphicsConfiguration,
				int eye) {
			super(graphicsConfiguration, true);
			this.eye = eye;

			this.setMonoscopicViewPolicy(eye);
		}

		// To be called on the EDT only !!!!!!!!!!!!!!
		/**
		 * Creates an offscreen buffer to be attached to the heavyweight buffer.
		 * Buffer is created 'byreference'
		 * 
		 * @param width
		 *            the width of the buffer.
		 * @param height
		 *            the height of the buffer.
		 */
		private void createOffScreenBuffers(Rectangle screenRect, int width,
				int height) {

			// System.out.println("JCanvas3DAnaglyphs createOffScreenBuffers 0 eye = "
			// + eye);
			// Now the J3D-Renderer thread can't enter the repaint code block in
			// postSwap
			this.stopRenderer();

			// Compute physical dimensions of the screen
			// Fix to Issue : 433 - JCanvas3D crashed when using jogl pipe.
			int screenWidth = (int) screenRect.getWidth();
			int screenHeight = (int) screenRect.getHeight();
			Screen3D screen3D = this.getScreen3D();
			screen3D.setSize(screenWidth, screenHeight);
			screen3D.setPhysicalScreenWidth(((double) screenWidth)
					* METERS_PER_PIXEL);
			screen3D.setPhysicalScreenHeight(((double) screenHeight)
					* METERS_PER_PIXEL);

			if (renderImage != null)
				renderImage.flush();
			if (eye == View.LEFT_EYE_VIEW && paintImage != null)
				paintImage.flush();

			// OffScreenBuffer: byReference & yUp
			renderImage = new BufferedImage(width, height,
					BufferedImage.TYPE_INT_ARGB);
			ImageComponent2D image = new ImageComponent2D(
					ImageComponent2D.FORMAT_RGBA, renderImage, true, true);
			image.setCapability(ImageComponent2D.ALLOW_IMAGE_READ);
			image.setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);

			if (eye == View.LEFT_EYE_VIEW) {
				paintImage = new BufferedImage(width, height,
						BufferedImage.TYPE_INT_ARGB);

				paintIntBuffer = ((DataBufferInt) paintImage.getRaster()
						.getDataBuffer()).getData();
				renderIntBufferLeft = ((DataBufferInt) renderImage.getRaster()
						.getDataBuffer()).getData();

				imageWidth = width;
				imageHeight = height;
			} else {
				renderIntBufferRight = ((DataBufferInt) renderImage.getRaster()
						.getDataBuffer()).getData();
			}

			// Offscreen rendering might occur even if the renderer is stopped.
			// For that reason, we are waiting for a hypothetical offscreen
			// render
			// to finish before setting offscreen rendering.
			// Otherwise, rendering will stop with an exception.
			this.waitForOffScreenRendering();

			try {
				// EDT will wait (sleep) until exchange of offscreen buffer is
				// done
				this.setOffScreenBuffer(image);
			} catch (RestrictedAccessException e) {
				// TODO Try again, riks of endless loop ?
				createOffScreenBuffers(screenRect, width, height);
				System.out
						.println("Repeat : createOffScreenBuffer(screenRect, width, height) !!");
			}

			this.startRenderer();

			// System.out.println("JCanvas3DAnaglyphs createOffScreenBuffers 1 eye = "
			// + eye);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void addNotify() {
			if (addNotifyFlag) {
				addNotifyFlag = false;
				super.addNotify();
			} else {
				throw new UnsupportedOperationException("CHANGE ME");
			}
		}

		/**
		 * Normally, returns the parent of that component. As the canvas ought
		 * to never be added to any component, it has no parent. Java 3D expects
		 * it to have a parent for some operations, so we in fact cheat it by
		 * returning the parent of the lightweight component.
		 * 
		 * @return the parent of the lightweight component, if any. Returns null
		 *         if it has no parent.
		 */
		@Override
		public Container getParent() {
			return JCanvas3DAnaglyph.this.getParent();
		}

		/**
		 * Retrieves the buffer from canvas, if possible, and calls/notifies
		 * component to be repainted, if necessary.
		 */
		@Override
		public void postSwap() {

			// System.out.println("JCanvas3DAnaglyphs postSwap eye = " + eye);
			postSwapDone(eye);
		}

		//
		// Override Component's processXXX
		//
		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processComponentEvent(ComponentEvent e) {
			super.processComponentEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processFocusEvent(FocusEvent e) {
			super.processFocusEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processInputMethodEvent(InputMethodEvent e) {
			super.processInputMethodEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processKeyEvent(KeyEvent e) {
			super.processKeyEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processMouseEvent(MouseEvent e) {
			super.processMouseEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processMouseMotionEvent(MouseEvent e) {
			super.processMouseMotionEvent(e);
		}

		/**
		 * Overriden so that the JComponent can access it.
		 * 
		 * @param e
		 *            {@inheritDoc}
		 */
		@Override
		protected void processMouseWheelEvent(MouseWheelEvent e) {
			super.processMouseWheelEvent(e);
		}
	}
}